/* This is a small demo of the NetX SNMP Agent on the high-performance NetX TCP/IP stack.  
   This demo relies on ThreadX and NetX to show simple SNMP GET/GETNEXT/SET requests on 
   the SNMP MIB-2 objects.  */

#include  "tx_api.h"
#include  "nx_api.h"
#include  "nx_snmp.h"
#include  "demo_snmp_helper.h"

#define     DEMO_STACK_SIZE         4096    
#define     AGENT_PRIMARY_ADDRESS   IP_ADDRESS(1,2,3,4) 

/* Define the ThreadX and NetX object control blocks...  */

TX_THREAD               thread_0;
NX_PACKET_POOL          pool_0;
NX_IP                   ip_0;
NX_SNMP_AGENT           my_agent;



/* To use SNMPv3 features such as authentication and encryption, define NX_SNMP_DISABLE_V3.  */

/* Define authentication and privacy keys.  */

#ifdef AUTHENTICATION_REQUIRED
NX_SNMP_SECURITY_KEY    my_authentication_key;
#endif

#ifdef PRIVACY_REQUIRED
NX_SNMP_SECURITY_KEY    my_privacy_key;
#endif

/* Define an error counter variable. */
UINT error_counter = 0;

/* This binds a secondary interfaces to the primary IP network interface 
   if SNMP is required for required for that interface. */
/* #define  MULTI_HOMED_DEVICE */

/* Define function prototypes.  A generic ram driver is used in this demo.  However to properly 
   run an SNMP agent demo, a real driver should be substituted. */

VOID    thread_agent_entry(ULONG thread_input);
VOID    _nx_ram_network_driver(NX_IP_DRIVER *driver_req_ptr);
UINT    mib2_get_processing(NX_SNMP_AGENT *agent_ptr, UCHAR *object_requested, NX_SNMP_OBJECT_DATA *object_data);
UINT    mib2_getnext_processing(NX_SNMP_AGENT *agent_ptr, UCHAR *object_requested, NX_SNMP_OBJECT_DATA *object_data);
UINT    mib2_set_processing(NX_SNMP_AGENT *agent_ptr, UCHAR *object_requested, NX_SNMP_OBJECT_DATA *object_data);
UINT    mib2_username_processing(NX_SNMP_AGENT *agent_ptr, UCHAR *username);
VOID    mib2_variable_update(NX_IP *ip_ptr, NX_SNMP_AGENT *agent_ptr);


UCHAR context_engine_id[] = {0x80, 0x00, 0x0d, 0xfe, 0x03, 0x00, 0x11, 0x23, 0x23, 0x44, 0x55};
UINT  context_engine_size = 11;
UCHAR context_name[] = {0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c};
UINT  context_name_size = 7;

/* Define main entry point.  */

int main()
{

    /* Enter the ThreadX kernel.  */
    tx_kernel_enter();
}


/* Define what the initial system looks like.  */
void    tx_application_define(void *first_unused_memory)
{

UCHAR   *pointer;
UINT    status;


    /* Setup the working pointer.  */
    pointer =  (UCHAR *) first_unused_memory;

    status = tx_thread_create(&thread_0, "agent thread", thread_agent_entry, 0,  
            pointer, DEMO_STACK_SIZE, 
            4, 4, TX_NO_TIME_SLICE, TX_AUTO_START);
    if (status != NX_SUCCESS)
    {
         return;
    }

    pointer =  pointer + DEMO_STACK_SIZE;


    /* Initialize the NetX system.  */
    nx_system_initialize();

    /* Create packet pool.  */
    status = nx_packet_pool_create(&pool_0, "NetX Packet Pool 0", 2048, pointer, 20000);

    if (status != NX_SUCCESS)
    {
         return;
    }

    pointer = pointer + 20000;

    /* Create an IP instance.  */
    status = nx_ip_create(&ip_0, "SNMP Agent IP Instance", AGENT_PRIMARY_ADDRESS, 
                        0xFFFFFF00UL, &pool_0, _nx_ram_network_driver,
                        pointer, 4096, 1);

    if (status != NX_SUCCESS)
    {
         return;
    }

    pointer =  pointer + 4096;

    /* Enable ARP and supply ARP cache memory for IP Instance 0.  */
    nx_arp_enable(&ip_0, (void *) pointer, 1024);
    pointer = pointer + 1024;

    /* Enable ICMP for ping.  */
    nx_icmp_enable(&ip_0);
  
    /* Enable UPD processing for IP instance.  */
    nx_udp_enable(&ip_0);

    /* Create an SNMP agent instance.  */
    status = nx_snmp_agent_create(&my_agent, "SNMP Agent", &ip_0, pointer, 4096, &pool_0, 
                                  mib2_username_processing, mib2_get_processing, mib2_getnext_processing, 
                                  mib2_set_processing);

    if (status != NX_SUCCESS)
    {
         return;
    }

    pointer =  pointer + 4096;

#ifdef NX_SNMP_DISABLE_V3
    status = nx_snmp_agent_context_engine_set(&my_agent, context_engine_id, context_engine_size);

    if (status != NX_SUCCESS)
    {
         error_counter++;
    }
#endif /* NX_SNMP_DISABLE_V3 */

    return;
}

VOID thread_agent_entry(ULONG thread_input)
{

UINT status;

    NX_PARAMETER_NOT_USED(thread_input);

    /* Allow NetX time to get initialized. */
    tx_thread_sleep(NX_IP_PERIODIC_RATE);


#ifdef AUTHENTICATION_REQUIRED
    
    /* Create an authentication key.  */
    status =  nx_snmp_agent_md5_key_create(&my_agent, (UCHAR *)"authpassword", &my_authentication_key);
    if (status)
      error_counter++;

    /* Use the authentication key.  */
    status =  nx_snmp_agent_authenticate_key_use(&my_agent, &my_authentication_key);
    if (status)
      error_counter++;
    
#endif

#ifdef PRIVACY_REQUIRED

    /* Create a privacy key.  */
    status = nx_snmp_agent_md5_key_create(&my_agent, (UCHAR *)"privpassword", &my_privacy_key);
    if (status)
      error_counter++;
    
    /* Use the privacy key.  */
    status = nx_snmp_agent_privacy_key_use(&my_agent, &my_privacy_key);
    if (status)
      error_counter++;
#endif

    /* Start the SNMP instance.  */
    status = nx_snmp_agent_start(&my_agent);
    if (status)
      error_counter++;

}

/* Define the application's GET processing routine.  */

UINT    mib2_get_processing(NX_SNMP_AGENT *agent_ptr, UCHAR *object_requested, NX_SNMP_OBJECT_DATA *object_data)
{

UINT    i;
UINT    status;

    NX_PARAMETER_NOT_USED(agent_ptr);


    /* Loop through the sample MIB to see if we have information for the supplied variable.  */
    i =  0;
    status =  NX_SNMP_ERROR;
    while (mib2_mib[i].object_name)
    {

        /* See if we have found the matching entry.  */
        status =  nx_snmp_object_compare(object_requested, mib2_mib[i].object_name);

        /* Was it found?  */
        if (status == NX_SUCCESS)
        {

            /* Yes it was found.  */
            break;
        }

        /* Move to the next index.  */
        i++;
    }

    /* Determine if a not found condition is present.  */
    if (status != NX_SUCCESS)
    {


        /* The object was not found - return an error.  */
        return(NX_SNMP_ERROR_NOSUCHNAME);
    }

    /* Determine if the entry has a get function.  */
    if (mib2_mib[i].object_get_callback)
    {

        /* Yes, call the get function.  */
        status =  (mib2_mib[i].object_get_callback)(mib2_mib[i].object_value_ptr, object_data);
    }
    else
    {


        /* No get function, return no access.  */
        status =  NX_SNMP_ERROR_NOACCESS;
    }


    
    /* Return the status.  */
    return(status);
}


/* Define the application's GETNEXT processing routine.  */

UINT    mib2_getnext_processing(NX_SNMP_AGENT *agent_ptr, UCHAR *object_requested, NX_SNMP_OBJECT_DATA *object_data)
{

UINT    i;
UINT    status;

    NX_PARAMETER_NOT_USED(agent_ptr);



    /* Loop through the sample MIB to see if we have information for the supplied variable.  */
    i =  0;
    status =  NX_SNMP_ERROR;
    while (mib2_mib[i].object_name)
    {

        /* See if we have found the next entry.  */
        status =  nx_snmp_object_compare(object_requested, mib2_mib[i].object_name);

        /* Is the next entry the mib greater?  */
        if (status == NX_SNMP_NEXT_ENTRY)
        {

            /* Yes it was found.  */
            break;
        }

        /* Move to the next index.  */
        i++;
    }

    /* Determine if a not found condition is present.  */
    if (status != NX_SNMP_NEXT_ENTRY)
    {


        /* The object was not found - return an error.  */
        return(NX_SNMP_ERROR_NOSUCHNAME);
    }


    /* Copy the new name into the object.  */
    nx_snmp_object_copy(mib2_mib[i].object_name, object_requested);


    /* Determine if the entry has a get function.  */
    if (mib2_mib[i].object_get_callback)
    {

        /* Yes, call the get function.  */
        status =  (mib2_mib[i].object_get_callback)(mib2_mib[i].object_value_ptr, object_data);

        /* Determine if the object data indicates an end-of-mib condition.  */
        if (object_data -> nx_snmp_object_data_type == NX_SNMP_END_OF_MIB_VIEW)
        {

            /* Copy the name supplied in the mib table.  */
            nx_snmp_object_copy(mib2_mib[i].object_value_ptr, object_requested);
        }
    }
    else
    {


        /* No get function, return no access.  */
        status =  NX_SNMP_ERROR_NOACCESS;
    }


    
    /* Return the status.  */
    return(status);
}


/* Define the application's SET processing routine.  */

UINT    mib2_set_processing(NX_SNMP_AGENT *agent_ptr, UCHAR *object_requested, NX_SNMP_OBJECT_DATA *object_data)
{

UINT    i;
UINT    status;

    NX_PARAMETER_NOT_USED(agent_ptr);


    /* Loop through the sample MIB to see if we have information for the supplied variable.  */
    i =  0;
    status =  NX_SNMP_ERROR;
    while (mib2_mib[i].object_name)
    {

        /* See if we have found the matching entry.  */
        status =  nx_snmp_object_compare(object_requested, mib2_mib[i].object_name);

        /* Was it found?  */
        if (status == NX_SUCCESS)
        {

            /* Yes it was found.  */
            break;
        }

        /* Move to the next index.  */
        i++;
    }

    /* Determine if a not found condition is present.  */
    if (status != NX_SUCCESS)
    {


        /* The object was not found - return an error.  */
        return(NX_SNMP_ERROR_NOSUCHNAME);
    }


    /* Determine if the entry has a set function.  */
    if (mib2_mib[i].object_set_callback)
    {

        /* Yes, call the set function.  */
        status =  (mib2_mib[i].object_set_callback)(mib2_mib[i].object_value_ptr, object_data);
    }
    else
    {


        /* No get function, return no access.  */
        status =  NX_SNMP_ERROR_NOACCESS;
    }

    
    /* Return the status.  */
    return(status);
}


/* Define the application's authentication routine.  */

UINT  mib2_username_processing(NX_SNMP_AGENT *agent_ptr, UCHAR *username)
{

    NX_PARAMETER_NOT_USED(agent_ptr);
    NX_PARAMETER_NOT_USED(username);

    /* Update MIB-2 objects. In this example, it is only the SNMP objects.  */
    mib2_variable_update(&ip_0, &my_agent);

    /* No authentication is done, just return success!  */
    return(NX_SUCCESS);
}


/* Define the application's update routine.  */ 

VOID  mib2_variable_update(NX_IP *ip_ptr, NX_SNMP_AGENT *agent_ptr)
{

    NX_PARAMETER_NOT_USED(ip_ptr);
    NX_PARAMETER_NOT_USED(agent_ptr);

    /* This section is for updating the snmp parameters defined in the MIB table definition files.  */
}

